package pers.mihao.quickstream.common.util.spring;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import pers.mihao.quickstream.common.exception.BaseException;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * applicationContextHolder
 * 以静态变量保存Spring ApplicationContext 方便在不将单例类交给Spring 管理时使用被Spring 管理的类
 *
 * @author mihao
 */
@Configuration
public class ApplicationContextHolder implements InitializingBean {

    private static ApplicationContextHolder _this;

    @Resource
    public SpringAllAwareRegister.SpringAllAware springAllAware;


    public static ClassLoader getClassLoader() {
        return _this.springAllAware.getClassLoader();
    }

    public static BeanFactory getBeanFactory() {
        return _this.springAllAware.getBeanFactory();
    }


    public static Environment getEnvironment() {
        return _this.springAllAware.getEnvironment();
    }

    public static ResourceLoader getResourceLoader() {
        return _this.springAllAware.getResourceLoader();
    }

    static ApplicationContext getApplicationContext() {
        checkApplicationContext();
        return _this.springAllAware.getApplicationContext();
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     *
     * @param name
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> tClass) {
        checkApplicationContext();
        return _this.springAllAware.getApplicationContext().getBean(name, tClass);
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.如果有多个Bean符合Class, 取出第一个.
     *
     * @param clazz
     * @param <T>
     * @return
     */
    static <T> T getBeanOrNull(Class<T> clazz) {
        if (_this.springAllAware.getApplicationContext() == null) {
            return null;
        }
        Map<String, T> beanMaps = _this.springAllAware.getApplicationContext().getBeansOfType(clazz);
        if (!beanMaps.isEmpty()) {
            return beanMaps.values().iterator().next();
        } else {
            return null;
        }
    }

    public static <T> T getBean(Class<T> clazz) {
        checkApplicationContext();
        T bean = getBeanOrNull(clazz);
        if (bean == null) {
            throw new BaseException("Spring bean容器中不存在对象" + clazz);
        }
        return bean;
    }

    public static <T> List<T> getBeans(Class<T> clazz) {
        return _this.springAllAware.getApplicationContext() == null ? new ArrayList<>() :
                new ArrayList<>(_this.springAllAware.getApplicationContext().getBeansOfType(clazz).values());
    }

    private static void checkApplicationContext() {
        if (_this.springAllAware.getApplicationContext() == null) {
            throw new BaseException("applicationContext未注入");
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        _this = this;
    }
}